#!/usr/bin/env python
# -*- coding:utf-8 -*-

import os
import time
import logging

from cgi import parse_qs,parse_qsl
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext.webapp.util import login_required
from google.appengine.api import mail

from lib.libgreader import *
from lib.tornado import template
from lib.tornado import escape
from lib.BeautifulSoup import BeautifulSoup
import lib.oauth2 as oauth


class GoogleReaderUser(db.Expando):
    kindle_email = db.StringProperty()
    email = db.StringProperty()
    categories = db.StringListProperty()
    make_read = db.BooleanProperty()
    max_item  = db.IntegerProperty()
    auth_method = db.StringProperty() # client or oauth
    username = db.StringProperty()
    password = db.StringProperty()
    oauth_token = db.StringProperty()
    oauth_token_secret = db.StringProperty()
    access_token = db.StringProperty()
    access_secret = db.StringProperty()

class DeliverLog(db.Model):
    sender = db.StringProperty()
    to = db.StringProperty()
    time = db.StringProperty()
    feeds = db.IntegerProperty()
    items = db.IntegerProperty()
    
class GoogleOAuth(db.Model):
    consumer_key = db.StringProperty()
    consumer_secret = db.StringProperty()

class BaseHandler(webapp.RequestHandler):
    
    gOAuth = None
    
    def get_greader_auth(self, gr):
        """docstring for get_greader_auth"""
        if hasattr(gr, 'auth_method') and gr.auth_method == 'oauth':
            
            if not self.gOAuth:
                self.gOAuth = GoogleOAuth.get_by_key_name('kindlereader')
            
            if not self.gOAuth:
                return None
            
            return OAuth2Method(self.gOAuth.consumer_key, self.gOAuth.consumer_secret, gr.access_token, gr.access_secret)
        else:
            return ClientAuth(gr.username, gr.password)
            
    def get_greader_user(self, email):
        """docstring for get_greader_user"""
        
        gr = GoogleReaderUser.get_by_key_name(email)
        
        if not gr:
            gr = GoogleReaderUser(key_name=email)
            gr.email = email
            gr.kindle_email = ""
            gr.categories = []
            gr.make_read = False
            gr.max_item = 50
        
        return gr
    
    def render(self, tempate_name, write=True, **args):
        user = users.get_current_user()
        
        if user:
            nickname = user.nickname()
        else:
            nickname = ''
        
        loader = template.Loader(os.path.join(os.path.dirname(__file__), 'views'))
        html_data = loader.load(tempate_name).generate(
                        logout_url=users.create_logout_url("/"),
                        nickname=nickname,
                        **args)
        
        if write:
            self.response.out.write(html_data)
        else:
            return html_data
        
class MainHandler(BaseHandler):
    
    def get(self, success=False):
        user = users.get_current_user()
        
        if not user or not users.is_current_user_admin():
            self.render('welcome.html', login_url=users.create_login_url("/"))
        else:
            gr = GoogleReaderUser.get_by_key_name(user.email())
            
            gr_user, categories = None, None
            if gr:
                try:
                    auth = self.get_greader_auth(gr)
                    reader = GoogleReader(auth)
                    gr_user = reader.getUserInfo()
                    reader.buildSubscriptionList()
                    categories = reader.getCategories()
                except:
                   pass

            self.render('admin.html',
                    user=user.nickname(),
                    gr=gr,
                    gr_user=gr_user,
                    categories=categories,
                    success = success
                )
                
    def post(self):
        """docstring for post"""
        
        user = users.get_current_user()
        
        if not user or not users.is_current_user_admin():
            self.redirect("/")
        else:
            kindle_email = self.request.get("kindle_email")
            email = self.request.get("email")
            make_read = self.request.get("make_read")
            categories = self.request.get_all("categories")
            max_item = self.request.get("max_item")
            
            if make_read and make_read == '1':
                make_read = True
            else:
                make_read = False
                
            if not categories:
                categories = []
                
            if not max_item.isdigit():
                max_item = 50
            else:
                max_item = int(max_item)
        
            gr = GoogleReaderUser.get_by_key_name(user.email())
            gr.kindle_email = kindle_email
            gr.email = email
            gr.categories = categories
            gr.make_read = make_read
            gr.max_item = max_item

            gr.put()
            self.get(True)
            
class AuthHandler(BaseHandler):
    
    def get(self):
        self.render('auth.html')
        
class ClientAuthHandler(BaseHandler):
    """docstring for GoogleReaderHandler"""
    
    def get(self):
        """docstring for get"""
        user = users.get_current_user()
        
        if not user or not users.is_current_user_admin():
            self.redirect("/")
            
        gr = GoogleReaderUser.get_by_key_name(user.email())
        self.render('gr.html', auth_method='client', gr=gr, success=False, fail=False)
            
    def post(self):
        
        user = users.get_current_user()
        
        if not user or not users.is_current_user_admin():
            self.redirect("/")
            
        username = self.request.get("username")
        password = self.request.get("password")
        
        gr_user = None
        if username and password:
            try:
                auth = ClientAuth(username, password)
                reader = GoogleReader(auth)
                gr_user = reader.getUserInfo()
            except:
                pass
        
        if not gr_user:
            gr = GoogleReaderUser()
            gr.username = username
            self.render('gr.html', auth_method='client', gr=gr, success=False, fail=True)
        else:
            gr = self.get_greader_user(user.email())
            
            gr.username = username
            gr.password = password
            gr.access_token = None
            gr.access_secret = None
            gr.auth_method = 'client'
            gr.put()
            
            self.render('gr.html', auth_method='client', success=True, gr=gr, fail=False)

class OAuthSettingHandler(BaseHandler):
    """docstring for GoogleReaderHandler"""

    def get(self):
        """docstring for get"""
        user = users.get_current_user()
        goa = GoogleOAuth.get_by_key_name('kindlereader')
        self.render('oauth_setting.html', fail=False, success=False, goa=goa)
    
    def post(self):
        """docstring for post"""
        
        consumer_key = self.request.get("consumer_key")
        consumer_secret = self.request.get("consumer_secret")
        
        goa = GoogleOAuth.get_by_key_name('kindlereader')
        
        if not goa:
            goa = GoogleOAuth(key_name='kindlereader')
        
        goa.consumer_key = consumer_key
        goa.consumer_secret = consumer_secret
        goa.put()
        
        self.render('oauth_setting.html', fail=False, success=True, goa=goa)
          
class OAuthAuthHandler(BaseHandler):
    """docstring for OAuthHandler"""

    def get(self, mode=None):
        """docstring for get"""
        
        user = users.get_current_user()
        
        gOAuth = GoogleOAuth.get_by_key_name('kindlereader')
        
        if not gOAuth:
            self.redirect("/auth/oauth/setting")
            return
        
        consumer = oauth.Consumer(gOAuth.consumer_key, gOAuth.consumer_secret)
        
        if mode == 'verifer':
            
            oauth_token = self.request.get('oauth_token')
            gr = self.get_greader_user(user.email())
            
            if oauth_token != gr.oauth_token:
                self.redirect("/")
                return 
            
            token = oauth.Token(gr.oauth_token, gr.oauth_token_secret)
            client = oauth.Client(consumer, token)
            resp, content = client.request(OAuthMethod.ACCESS_TOKEN_URL, "POST")
            
            if resp['status'] == '200':
                access_token = dict(parse_qsl(content))
            
                gr.access_token = access_token['oauth_token']
                gr.access_secret = access_token['oauth_token_secret']
                gr.oauth_token = None
                gr.oauth_token_secret = None
                gr.username = None
                gr.password = None
                gr.auth_method = 'oauth'
                gr.put()
            
                self.render('gr.html', auth_method='oauth', success=True, gr=gr, fail=False)
            else:
                self.render('gr.html', auth_method='oauth', success=False, gr=gr, fail=True)
        else:
            client = oauth.Client(consumer)
            resp, content = client.request(OAuthMethod.REQUEST_TOKEN_URL, "GET")
            
            if resp['status'] == '200':
                
                request_token = dict(parse_qsl(content))
                gr = self.get_greader_user(user.email())
            
                gr.oauth_token = request_token['oauth_token']
                gr.oauth_token_secret = request_token['oauth_token_secret']
                gr.put()

                authorize_url = "%s?oauth_token=%s&oauth_callback=%s" % (OAuthMethod.AUTHORIZE_URL, request_token['oauth_token'],'%s/verifer' % self.request.url)
                
                self.render('gr.html', auth_method='oauth', success=False, gr=gr, fail=False, authorize_url=authorize_url)
        
class DeliverHandler(BaseHandler):
    
    remove_tags = ['img','script', 'object','video','embed','iframe','noscript']
    remove_attrs = ['class','id','title','width','height','onclick','onload']
    
    def get(self):
        """docstring for get"""
        
        user = users.get_current_user()
        
        gr = GoogleReaderUser()
        gr_users = gr.all()
        for gr_user in gr_users:
            if gr_user.kindle_email \
                and gr_user.categories:
                
                try:
                    self.process(gr_user)
                except Exception, e:
                    logging.error(e)
        
    def process(self, gr):
        
        auth = self.get_greader_auth(gr)
        reader = GoogleReader(auth)
        gr_user = reader.getUserInfo()
        reader.buildSubscriptionList()
        categories = reader.getCategories()
        
        feeds = {}
        for category in categories:
        
            if category.id in gr.categories:
                fd = category.getFeeds()
                for f in fd:
                    if f.id not in feeds:
                        feeds[f.id] = f
                fd = None
                
        idx, work_idx, updated_items = 1, 1, 0
        feed_num, current_feed = len(feeds), 0
        updated_feeds = []
        
        for feed_id in feeds:
            feed = feeds[feed_id]

            current_feed = current_feed + 1
            
            try:
                feed_data = reader.getFeedContent(feed, True, number=gr.max_item)
            
                item_idx = 1
                for item in feed_data['items']:
                
                    item['idx'] = item_idx
                    item = Item(reader, item, feed)
                    
                    if hasattr(item, 'content'):
                        item.content = self.parse_content(item.content)
                    
                    if gr.make_read:
                        item.markRead()
                
                    item_idx = item_idx + 1

                feed.item_count = len(feed.items)
                updated_items = updated_items + feed.item_count

                if feed.item_count > 0:
                    feed.idx = idx
                    #reader.markFeedAsRead(feed)
                    updated_feeds.append(feed)
                    idx = idx + 1
                    logging.debug("update %s items." % feed.item_count)
                else:
                    logging.debug("no update.")
                    
            except Exception, e:
                logging.error("fail:" % e)
                
        if updated_items > 0:
            html_data = self.render('mail.html',
                            write=False,
                            user = gr_user,
                            feeds = updated_feeds,
                        )
                        
            self.sendmail(gr.email, gr.kindle_email, gr_user['userName'], html_data)
        
        DeliverLog(sender=gr.email, to=gr.kindle_email, time=time.strftime('%Y-%m-%d %H:%M:%S'), feeds=len(updated_feeds), items=updated_items).put()
        
        logging.info('deliver to %s finished, update %s items of %s feeds' % (gr.kindle_email, updated_items, len(updated_feeds)))
        
        test = self.request.get('test')
        if test:
            if updated_items > 0:
                self.response.out.write(html_data)
            else:
                self.response.out.write("no update.")
        else:
            self.response.out.write("ok.")
    
    def parse_content(self, content):
        
        soup = BeautifulSoup(content)
        
        for tag in soup.findAll(self.remove_tags):
            tag.extract()
                
        for attr in self.remove_attrs:
            for tag in soup.findAll(attrs={attr:True}):
                del tag[attr]

        content = soup.renderContents('utf-8')
        soup = None
            
        return content
        
    def sendmail(self, sender, to, user, data):
        """docstring for mail"""
        mail.send_mail(sender=sender,
                          to=to,
                          subject="Convert",
                          body="deliver %s's google reader" % user,
                          attachments=[("%s's google reader(%s).html" % (user, time.strftime('%m-%d %Hh%Mm')), data)])


def main():
    logging.getLogger().setLevel(logging.INFO)
    application = webapp.WSGIApplication([
                        ('/', MainHandler),
                        ('/auth', AuthHandler),
                        ('/auth/client', ClientAuthHandler),
                        ('/auth/oauth', OAuthAuthHandler),
                        ('/auth/oauth/(verifer)', OAuthAuthHandler),
                        ('/auth/oauth/setting', OAuthSettingHandler),
                        ('/deliver', DeliverHandler)],
                        debug=True)
    util.run_wsgi_app(application)


if __name__ == '__main__':
    main()